﻿namespace CookieTerm
{
    using System;
    using System.Collections.Generic;

    /// <summary>
    /// Represents display attributes of a character. Blink and bright are the only ones supported by CGA.
    /// </summary>
    public enum DisplayAttribute
    {
        Blink = 128,
        Bold = 8,
        Inverse = 1,
        Underline = 2
    }

    /// <summary>
    /// Represents all the 8 logical ANSI colors. Actual mapping to physical color is up to the terminal.
    /// </summary>
    public enum AnsiColor
    {
        Black = 0,
        Red = 1,
        Green = 2,
        Yellow = 3,
        Blue = 4,
        Magenta = 5,
        Cyan = 6,
        White = 7
    }

    /// <summary>
    /// This class simulates a CGA video buffer. It hides the implementation details from the console.
    /// </summary>
    public class FrameBuffer
    {
        private static CharAttribute BlankCell = new CharAttribute
        {
            Character = ' ',
            Background = AnsiColor.Black,
            Foreground = AnsiColor.White,
            Attributes = 0,
        };

        private int rows, columns;
        private List<CharAttribute[]> buffer;

        public CharAttribute this[int row, int column]
        {
            get
            {
                return this.buffer[row - 1][column - 1];
            }
        }

        public void SetCharAttributeAt(int row, int column, CharAttribute c)
        {
            lock (this)
            {
                this.buffer[row - 1][column - 1] = c;
            }
        }

        public void CreateBuffer(int rows, int columns)
        {
            lock (this)
            {
                this.rows = rows;
                this.columns = columns;

                this.buffer = new List<CharAttribute[]>(rows);
                for (int r = 1; r <= rows; r++)
                {
                    this.buffer.Add(CreateBlankRow());
                }
            }
        }

        public void ScrollUp()
        {
            lock (this)
            {
                this.buffer.Add(CreateBlankRow());
                this.buffer.RemoveAt(0);
            }
        }

        public void InsertLine(int row)
        {
            lock (this)
            {
                this.buffer.Insert(row - 1, CreateBlankRow());
                this.buffer.RemoveAt(this.rows);
            }
        }

        public CharAttribute[] CreateBlankRow()
        {
            CharAttribute[] result = new CharAttribute[this.columns];
            for (int i = 0; i < this.columns; i++)
            {
                result[i] = BlankCell;
            }
            return result;
        }
    }

    public struct CharAttribute
    {
        public char Character;
        public AnsiColor Foreground;
        public AnsiColor Background;
        public byte Attributes;
    }

    public static class AttributeExtension
    {
        public static bool HasBold(this byte attributes)
        {
            return (attributes & (byte)DisplayAttribute.Bold) != 0;
        }

        public static bool HasBlink(this byte attributes)
        {
            return (attributes & (byte)DisplayAttribute.Blink) != 0;
        }

        public static bool HasInverse(this byte attributes)
        {
            return (attributes & (byte)DisplayAttribute.Inverse) != 0;
        }

        public static bool HasUnderline(this byte attributes)
        {
            return (attributes & (byte)DisplayAttribute.Underline) != 0;
        }
    }
}
